package com.qsw.qswcommonlib.util.math;

/**
 * 直线及相关计算
 */
public class LineEX {
    private PointDEX mPointDEX1; // 线上的第一个点
    private PointDEX mPointDEX2; // 线上的第二个点

    public LineEX(double x1, double y1, double x2, double y2) {
        PointDEX p1 = new PointDEX(x1, y1);
        PointDEX p2 = new PointDEX(x2, y2);
        // 两点是否重合，重合则报错并退出
        if ((p1.getX() == p2.getX()) && p1.getY() == p2.getY()) {
            throw new IllegalArgumentException("(p1.getX() == p2.getX()) && p1.getY() == p2.getY()");
        }
        this.mPointDEX1 = p1;
        this.mPointDEX2 = p2;
    }

    public LineEX(PointDEX p1, PointDEX p2) {
        // 两点是否重合，重合则报错并退出
        if ((p1.getX() == p2.getX()) && p1.getY() == p2.getY()) {
            throw new IllegalArgumentException("(p1.getX() == p2.getX()) && p1.getY() == p2.getY()");
        }
        this.mPointDEX1 = p1;
        this.mPointDEX2 = p2;
    }

    /**
     * 获取线条的斜率
     *
     * @return 线条的斜率
     */
    public Double getSlope() {
        // （x1-x2=0）注意考虑斜率不存在即返回double类型无穷大"Infinite"
        return (mPointDEX2.getY() - mPointDEX1.getY()) / (mPointDEX2.getX() - mPointDEX1.getX());
    }

    /**
     * 获取两点距离
     *
     * @return 两点距离
     */
    public double getlength() {
        return Math.sqrt((mPointDEX1.getX() - mPointDEX2.getX()) * (mPointDEX1.getX() - mPointDEX2.getX()) + (mPointDEX1.getY() - mPointDEX2.getY()) * (mPointDEX1.getY() - mPointDEX2.getY()));
    }

    /**
     * 判断点P是否在线上
     *
     * @param pointDEX 另一个点
     * @return true：在线上；false：不在线上
     */
    public boolean isOnline(PointDEX pointDEX) {
        // 点重合
        if ((pointDEX.getX() == mPointDEX1.getX() && pointDEX.getY() == mPointDEX1.getY()) || (pointDEX.getX() == mPointDEX2.getX() && pointDEX.getY() == mPointDEX2.getY())) {
            return true;
        }
        LineEX lineEX = new LineEX(mPointDEX1, pointDEX);
        if (lineEX.getSlope().isInfinite() && this.getSlope().isInfinite()) {
            return true;
        }

        // 此点与线上任意一点构成的线的斜率相等则此点在线上
        double slope1 = lineEX.getSlope();
        double slope2 = this.getSlope();

        return Math.abs(slope1 - slope2) < 0.00000000001;
    }

    /**
     * 获取点p到线的距离（最短距离，即垂线）
     *
     * @param pointDEX 另一个点
     * @return 点p到线的距离（最短距离，即垂线）
     */
    public double getDistance(PointDEX pointDEX) {
        return getDistance(pointDEX.getX(), pointDEX.getY());
    }

    /**
     * 获取点p到线的距离（最短距离，即垂线）
     *
     * @param px 点p X坐标
     * @param py 点p Y坐标
     * @return 点p到线的距离（最短距离，即垂线）
     */
    public double getDistance(double px, double py) {
        // 利用两点求直线方程，利用公式代入即可
        // 直线方程x(y2-y1)-y(x2-x1)-x1(y2-y1)+y1(x2-x1)=0
        double distY = mPointDEX2.getY() - mPointDEX1.getY();
        double distX = mPointDEX2.getX() - mPointDEX1.getX();
        return Math.abs(px * distY - py * distX - mPointDEX1.getX() * distY + mPointDEX1.getY() * distX)
                / mPointDEX1.getDistance(mPointDEX2);
    }

    /**
     * 判断P是否在线上且在两点之间；与端点重合，认为不在在两点之间
     *
     * @param pointDEX 另一个点
     * @return true：在两点之间；false：不在两点之间
     */
    public boolean isBetween(PointDEX pointDEX) {
        if (!this.isOnline(pointDEX)) {
            return false;
        }
        // 与端点重合，认为不在在两点之间
        if (pointDEX.equals(mPointDEX1) || pointDEX.equals(mPointDEX2)) {
            return false;
        }
        // p到 p1和p2的距离 同时小于 p1到p2的距离 说明 交点在 p1到p2的线段上
        double p1p2Distance = mPointDEX2.getDistance(mPointDEX1);
        return pointDEX.getDistance(mPointDEX2) < p1p2Distance && pointDEX.getDistance(mPointDEX1) < p1p2Distance;
    }

    /**
     * 判断p1、p2是否在p的同一侧
     *
     * @param pointDEX 另一个点
     * @return true：在同一侧；false：不在同一侧
     */
    public boolean isSameSide(PointDEX pointDEX) {
        // 点在线上且不在点之间
        return isOnline(pointDEX) && !isBetween(pointDEX);
    }

    /**
     * 获取p1、p2之间的中点
     *
     * @return p1、p2之间的中点
     */
    public PointDEX getMiddlePoint() {
        PointDEX p = new PointDEX();
        p.setX((mPointDEX1.getX() + mPointDEX2.getX()) / 2);
        p.setY((mPointDEX1.getY() + mPointDEX2.getY()) / 2);
        return p;
    }

    /**
     * 获取线段的第一个坐标点
     *
     * @return 线段的第一个坐标点
     */
    public PointDEX getPoint1() {
        return mPointDEX1;
    }

    /**
     * 获取线段的第二个坐标点
     *
     * @return 线段的第二个坐标点
     */
    public PointDEX getPoint2() {
        return mPointDEX2;
    }

    /**
     * 获取与线条l之间的夹角，若两条线段交叉（交叉点位于其中一条线的两点之间），取较小的夹角
     *
     * @param lineEX 另一条线
     * @return 夹角
     */
    public double getAngle(LineEX lineEX) {
        // 利用公式θ=arctan∣(k2- k1)/(1+ k1k2)∣，此时求较小的夹角
        double k2 = getSlope();
        double k1 = lineEX.getSlope();
        return Math.atan(Math.abs((k2 - k1) / (1 + k1 * k2))) * 180.0 / Math.PI;// 返回值为角度
    }

    /**
     * 判断与另一条线是否平行
     *
     * @param lineEX 另一条线
     * @return true：平行；false：不平行
     */
    public boolean isParallel(LineEX lineEX) {
        Double b1 = this.getSlope();
        Double b2 = lineEX.getSlope();
        if ((b1.isInfinite()) && (b2.isInfinite())) {
            return true;
        } else {
            return (this.getSlope().doubleValue() == lineEX.getSlope().doubleValue());
        }
    }

    /**
     * 判断与另一条线是否重合
     *
     * @param lineEX 另一条线
     * @return true：重合；false：不重合
     */
    public boolean isCoincide(LineEX lineEX) {
        if (!this.isParallel(lineEX)) {
            return false;
        }
        if (this.isOnline(lineEX.mPointDEX1)) {
            return true;
        }
        return false;
    }

    /**
     * 获取交点，若两条线平行，返回null
     *
     * @param lineEX 另一条线
     * @return 交叉点，若两条线平行，返回null
     */
    public PointDEX getIntersection(LineEX lineEX) {
        if (this.isParallel(lineEX)) {
            return null;
        }
        if (mPointDEX1.equals(lineEX.mPointDEX1) || mPointDEX1.equals(lineEX.mPointDEX2)) {
            return mPointDEX1;
        }
        if (mPointDEX2.equals(lineEX.mPointDEX1) || mPointDEX2.equals(lineEX.mPointDEX2)) {
            return mPointDEX2;
        }
        PointDEX p3 = lineEX.mPointDEX1;
        PointDEX p4 = lineEX.mPointDEX2;
        double x_member;
        double x_denominator;
        double y_member;
        double y_denominator;
        PointDEX cross_point = new PointDEX();
        x_denominator = p4.getX() * mPointDEX2.getY() - p4.getX() * mPointDEX1.getY() - p3.getX() * mPointDEX2.getY()
                + p3.getX() * mPointDEX1.getY() - mPointDEX2.getX() * p4.getY() + mPointDEX2.getX() * p3.getY()
                + mPointDEX1.getX() * p4.getY() - mPointDEX1.getX() * p3.getY();
        x_member = p3.getY() * p4.getX() * mPointDEX2.getX() - p4.getY() * p3.getX() * mPointDEX2.getX()
                - p3.getY() * p4.getX() * mPointDEX1.getX() + p4.getY() * p3.getX() * mPointDEX1.getX()
                - mPointDEX1.getY() * mPointDEX2.getX() * p4.getX() + mPointDEX2.getY() * mPointDEX1.getX() * p4.getX()
                + mPointDEX1.getY() * mPointDEX2.getX() * p3.getX() - mPointDEX2.getY() * mPointDEX1.getX() * p3.getX();

        if (x_denominator == 0) {
            cross_point.setX(0);
        } else {
            cross_point.setX(x_member / x_denominator);
        }

        y_denominator = p4.getY() * mPointDEX2.getX() - p4.getY() * mPointDEX1.getX() - p3.getY() * mPointDEX2.getX() + mPointDEX1.getX() * p3.getY() - mPointDEX2.getY() * p4.getX() + mPointDEX2.getY() * p3.getX() + mPointDEX1.getY() * p4.getX()
                - mPointDEX1.getY() * p3.getX();
        y_member = -p3.getY() * p4.getX() * mPointDEX2.getY() + p4.getY() * p3.getX() * mPointDEX2.getY() + p3.getY() * p4.getX() * mPointDEX1.getY() - p4.getY() * p3.getX() * mPointDEX1.getY()
                + mPointDEX1.getY() * mPointDEX2.getX() * p4.getY() - mPointDEX1.getY() * mPointDEX2.getX() * p3.getY() - mPointDEX2.getY() * mPointDEX1.getX() * p4.getY() + mPointDEX2.getY() * mPointDEX1.getX() * p3.getY();

        if (y_denominator == 0) {
            cross_point.setY(0);
        } else {
            cross_point.setY(y_member / y_denominator);
        }

        return cross_point; // 平行返回(0,0)
    }
}